using Microsoft.Extensions.DependencyInjection;

namespace Crux.Core.NHibernate;
/// <summary>
/// SessionFactoryHelm
/// </summary>
public class SessionFactoryHelm<TContext>
    : ISessionFactoryHelm<TContext>
    where TContext : NHibernateContext
{
    #region private fields

    private readonly ISessionFactory _factory;
    private readonly Type[] _interceptors;
    private readonly Configuration _configuration;
    private readonly IServiceProvider? _provider;
    #endregion  private fields

    #region .ctor
    /// <summary>
    /// .ctor
    /// </summary>
    /// <param name="options"></param>
    /// <param name="serviceProvider"></param>
    /// <exception cref="ArgumentNullException"></exception>
    public SessionFactoryHelm(
        IOptions<NHibernateOptions<TContext>> options,
        IServiceProvider? serviceProvider)
    {
        Requires.NotNull(options, nameof(options));
        Requires.NotNull(options.Value, nameof(options.Value));
        Requires.NotNull(options.Value.Configuration, nameof(options.Value.Configuration));

        var option = options.Value;

        _configuration = option.Configuration!;
        _factory = Initialize(_configuration);
        _provider = serviceProvider;
        _interceptors = option.Interceptors ?? Array.Empty<Type>();
    }

    /// <summary>
    /// Initialize
    /// </summary>
    /// <param name="configure"></param>
    /// <returns></returns>
    private ISessionFactory Initialize(Configuration configure)
    {
        configure.AddFilterDefinition(new FilterDefinition(NHibernateConstants.SoftDeleteConstants, "", new Dictionary<string, IType>(), true));

        foreach (var classMapping in configure.ClassMappings)
        {
            if (!SoftDeleteExtensions.IsSoftDelete(classMapping.ProxyInterface)) continue;

            classMapping.AddFilter(NHibernateConstants.SoftDeleteConstants, "deleted=0");
        }

        return configure.BuildSessionFactory();
    }
    #endregion .ctor

    #region implements ISessionFactoryHelm
    /// <summary>
    /// GetSession
    /// </summary>
    /// <param name="configure"></param>
    /// <returns></returns>
    /// <exception cref="ArgumentNullException"></exception>
    public ISession GetSession(Action<ISessionBuilder>? configure = null)
    {
        var builder = _factory.WithOptions();
        builder.AutoJoinTransaction(true);

        configure?.Invoke(builder);
        if (builder is null)
        {
            throw new ArgumentNullException(nameof(configure));
        }

        if (_interceptors is not null)
        {
            foreach (var interceptorType in _interceptors)
            {
                var service = _provider?.GetService(interceptorType);
                if (service is not IInterceptor interceptor)
                {
                    continue;
                }

                builder = builder.Interceptor(interceptor);
            }
        }

        {
            var interceptor = _provider?.GetRequiredService<ServiceProviderInterceptor>();
            if (interceptor is not null)
            {
                builder = builder.Interceptor(interceptor);
            }
        }

        var session = builder.OpenSession();
        session.EnableFilter(NHibernateConstants.SoftDeleteConstants);

        return session;
    }
    #endregion implements ISessionFactoryHelm
}